/* http handlers for syncd server 
* TODO: ways to scale
*		- mirror feeds in parent-child relationships
*			- child notifies parent of new messages
*			- child subscribes to parent to maintain local copy
*			- parents redirect a subset of subscribers to child mirror
*		- steal feeds from parent node when not busy
* 		- parents redirect subscribers to children nodes
*
* TODO: real-time search
*
* known issues:
*		- DoS attacks possible by creating too many feeds; soln: remove empty/unused feeds after an interval?
*		- need way to authenticate child nodes before redirecting
*		- parents need way to monitor health of child nodes
*		
*
 */
package httpd

import (
	"hg.cloud9lib.net/cloud9lib.hg/rest"
	"hg.cloud9lib.net/cloud9lib.hg/syncd"
	"errors"
	"io/ioutil"
	"log"
	"net/http"

	"strconv"
)

var feeds map[string]*syncd.Feed = make(map[string]*syncd.Feed)
var clients map[string]string = make(map[string]string)

func AddFeed(id string) *syncd.Feed {

	f := syncd.NewFeed()
	feeds[id] = f
	return f
}

func Publish(id, uri string) error {

	f, ok := feeds[id]

	if !ok {
		return errors.New("feed does not exist: " + id)
	}

	f.Publish(uri)

	return nil
}

func postFeed(f *syncd.Feed, w http.ResponseWriter, req *http.Request) (int, error) {

	id := req.FormValue("id")

	if id == "" {
		return 400, errors.New("missing id parameter")
	}

	if f == nil {
		log.Println("OK creating new feed", id)

		f = syncd.NewFeed()
		feeds[id] = f
	}

	uri, err := ioutil.ReadAll(req.Body)

	if err != nil || len(uri) < 1 {
		return 400, errors.New("no uri in body")
	}

	log.Println(string(uri))

	f.Publish(string(uri))

	return 200, nil
}

func feedHandler(w http.ResponseWriter, req *http.Request) (int, error) {

	id := req.FormValue("id")

	if id == "" {
		return 400, errors.New("missing id parameter")
	}

	f, ok := feeds[id]

	if req.Method == "POST" {
		return postFeed(f, w, req)
	}

	if !ok {
		return 404, rest.NOT_FOUND
	}

	sincestr := req.FormValue("since")

	if sincestr == "" {
		sincestr = "0"
	}

	since, _ := strconv.ParseInt(sincestr, 10, 64)

	entries, ok := f.Since(since)

	blocking := req.FormValue("block") == "true"

	for blocking && !ok {
		clients[req.RemoteAddr] = req.URL.String()
		f.Wait()
		entries, ok = f.Since(since)
	}

	rest.WriteObject(w, req, entries)

	delete(clients, req.RemoteAddr)

	return 200, nil
}

func feedsHandler(w http.ResponseWriter, req *http.Request) (int, error) {
	return 400, rest.WriteObject(w, req, feeds)
}

func clientsHandler(w http.ResponseWriter, req *http.Request) (int, error) {
	return 200, rest.WriteObject(w, req, clients)
}

// side-effect: registers feed handlers
func init() {

	http.Handle("/feed", rest.HandlerFunc(feedHandler))
	http.Handle("/feeds", rest.HandlerFunc(feedsHandler))
	http.Handle("/clients", rest.HandlerFunc(clientsHandler))

	log.Println("OK registered syncd feed handlers")
}
